package com.ozonetel.util;

import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
import com.google.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder;
import com.ozonetel.MobileConstants;
import com.ozonetel.FLOperatorConstants;
import com.ozonetel.StdConstants;
import com.ozonetel.model.PhoneNumberInfo;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import net.sf.json.JSONObject;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author <a href="mailto:sudhakargupta.s@gmail.com">SudhakarGupta</a>
 */
public class PhoneNumberFormatter {

    private static PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance();
    protected transient static final Log log = LogFactory.getLog(PhoneNumberFormatter.class);

    public static boolean isValidNumber(String number) {
        return isValidNumber(number, "IN");
    }

    public static boolean isValidNumber(String number, String location) {
        boolean valid = false;
        log.debug("number ::: " + number + " ::: location ::: " + location);
        try {
            if (number != null && !number.trim().isEmpty()) {
                PhoneNumber phoneNumber = phoneNumberUtil.parse(number, location);
                valid = phoneNumberUtil.isValidNumber(phoneNumber);
            }
        } catch (NumberParseException e) {
            log.error(e.getMessage(), e);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        log.debug("number ::: " + number + " ::: isValid ::: " + valid);

        return valid;
    }

    public static JSONObject formats(String number) {
        return formats(number, "IN");
    }

    public static JSONObject formats(String number, String location) {
        log.debug("number ::: " + number + " ::: location ::: " + location);
        JSONObject formats = new JSONObject();
        try {
            PhoneNumber phoneNumber = phoneNumberUtil.parse(number, location);
            if (isValidNumber(number, location)) {
                for (PhoneNumberFormat phoneNumberFormat : PhoneNumberFormat.values()) {
                    formats.put(phoneNumberFormat.name(), phoneNumberUtil.format(phoneNumber, phoneNumberFormat).trim().replaceAll("\\s", ""));
                }
            }
        } catch (NumberParseException e) {
            log.error(e.getMessage(), e);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return formats;
    }

    public static String format(String number) {
        return format(number, "IN", PhoneNumberFormat.NATIONAL);
    }

    public static String format(String number, String location) {
        return format(number, location, PhoneNumberFormat.NATIONAL);
    }

    public static String format(String number, String location, PhoneNumberFormat format) {
        log.debug("number ::: " + number + " ::: location ::: " + location + " ::: format ::: " + format.name());
        /*try {
         PhoneNumber phoneNumber = phoneNumberUtil.parse(number, location);
         if (isValidNumber(number, location)) {
         String formattedNumber = phoneNumberUtil.format(phoneNumber, format).trim().replaceAll("\\s", "");
         log.debug("number ::: " + number + " ::: formattedNumber ::: " + formattedNumber);
         return formattedNumber;
         }
         } catch (NumberParseException e) {
         log.error(e.getMessage(), e);
         } catch (Exception e) {
         log.error(e.getMessage(), e);
         }*/
        JSONObject formats = formats(number, location);
        return (formats != null && !formats.isEmpty() ? (formats.get(format.name()) != null && !formats.get(format.name()).toString().trim().isEmpty() ? formats.get(format.name()).toString() : number) : number);
    }

    public static PhoneNumberInfo info(String number) {
        return info(number, "IN", PhoneNumberFormat.NATIONAL);
    }

    public static PhoneNumberInfo info(String number, String location) {
        return info(number, location, PhoneNumberFormat.NATIONAL);
    }

    public static PhoneNumberInfo info(String number, String location, PhoneNumberFormat format) {
        log.debug("number ::: " + number + " ::: location ::: " + location + " ::: format ::: " + format.name());
        PhoneNumberInfo phoneNumberInfo = new PhoneNumberInfo();
        phoneNumberInfo.setInputNumber(number);
        try {
            PhoneNumber phoneNumber = phoneNumberUtil.parse(number, location);
            if (isValidNumber(number, location)) {
                phoneNumberInfo.setValid(true);
                phoneNumberInfo.setValidForRegion(phoneNumberUtil.isValidNumberForRegion(phoneNumber, location));
                phoneNumberInfo.setRegion(phoneNumberUtil.getRegionCodeForNumber(phoneNumber));
                phoneNumberInfo.setCallType(phoneNumberInfo.isValidForRegion() ? PhoneNumberFormat.NATIONAL.name() : PhoneNumberFormat.INTERNATIONAL.name());
                phoneNumberInfo.setFormats(formats(number, location));
                //phoneNumberInfo.setInputNumber(phoneNumberUtil.format(phoneNumber, format).trim().replaceAll("\\s", ""));
                //log.debug("formattedNumber ::: " + phoneNumberInfo.getInputNumber());

                String numberType = phoneNumberUtil.getNumberType(phoneNumber).name().trim();
                log.debug("type ::: " + numberType);

                if (numberType != null && !numberType.trim().isEmpty()) {
                    if (phoneNumberInfo.isValidForRegion()) {
                        if (numberType.equals(PhoneNumberUtil.PhoneNumberType.MOBILE.name())
                                || numberType.equals(PhoneNumberUtil.PhoneNumberType.FIXED_LINE_OR_MOBILE.name())) {
                            //String numberLocation = MobileConstants.getConstant(extractMobileSeries(phoneNumberInfo.getPhoneNumber()));
                            String numberLocation = MobileConstants.getConstant(extractMobileSeries(phoneNumberInfo.getFormats().get(format.NATIONAL.name()).toString()));
                            if (numberLocation != null && !numberLocation.trim().isEmpty()) {
                                phoneNumberInfo.setOperator(numberLocation.split("~")[0].replaceAll("\\s", "").toUpperCase());
                                phoneNumberInfo.setLocation(numberLocation.split("~")[1].replaceAll("\\s", "").toUpperCase());
                                phoneNumberInfo.setType(PhoneNumberUtil.PhoneNumberType.MOBILE.name());
                            } else {
                                phoneNumberInfo = fixedLineNumberInfo(phoneNumber, phoneNumberInfo);
                            }
                        } else if (numberType.equals(PhoneNumberUtil.PhoneNumberType.FIXED_LINE.name())) {
                            phoneNumberInfo = fixedLineNumberInfo(phoneNumber, phoneNumberInfo);
                        } else {
                            phoneNumberInfo.setType(numberType);
                        }
                    } else {
                        if (numberType.equals(PhoneNumberUtil.PhoneNumberType.MOBILE.name())
                                || numberType.equals(PhoneNumberUtil.PhoneNumberType.FIXED_LINE_OR_MOBILE.name())) {
                            phoneNumberInfo.setType(PhoneNumberUtil.PhoneNumberType.MOBILE.name());
                        } else {
                            //phoneNumberInfo.setType(PhoneNumberUtil.PhoneNumberType.FIXED_LINE.name());
                            phoneNumberInfo.setType(numberType);
                        }
                    }
                }
            } else {
                if (number.length() > 10) {
                    phoneNumberInfo = info((number.startsWith("0") ? number.substring(1, 11) : number.substring(0, 10)), location, format);
                }
            }
        } catch (NumberParseException e) {
            log.error(e.getMessage(), e);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return phoneNumberInfo;
    }

    public static JSONObject jsonInfo(String number) {
        return jsonInfo(number, "IN", PhoneNumberFormat.NATIONAL);
    }

    public static JSONObject jsonInfo(String number, String location) {
        return jsonInfo(number, location, PhoneNumberFormat.NATIONAL);
    }

    public static JSONObject jsonInfo(String number, String location, PhoneNumberFormat format) {
        JSONObject jsonObject = JSONObject.fromObject(info(number, location, format));
        jsonObject.put("status", jsonObject.getBoolean("valid") ? "success" : "error");
        jsonObject.put("message", jsonObject.getBoolean("valid") ? "ValidNumber" : "InvalidNumber");
        return jsonObject;
    }

    public static PhoneNumberInfo fixedLineNumberInfo(PhoneNumber phoneNumber, PhoneNumberInfo phoneNumberInfo) {
        PhoneNumberOfflineGeocoder geocoder = PhoneNumberOfflineGeocoder.getInstance();
        String numberLocation = geocoder.getDescriptionForNumber(phoneNumber, Locale.ENGLISH);
        log.debug("numberLocation ::: " + numberLocation);
        if (numberLocation != null
                && !numberLocation.trim().isEmpty()) {
            if (numberLocation.split(",").length > 1) {
                phoneNumberInfo.setLocation(numberLocation.split(",")[1].trim().replaceAll("\\s", "").toUpperCase());
            } else {
                phoneNumberInfo.setLocation(numberLocation.trim().replaceAll("\\s", "").toUpperCase());
            }
        } else {
            phoneNumberInfo.setLocation(numberLocation);
        }
        //phoneNumberInfo.setOperator(extractFixedLineOperator(phoneNumberInfo.getPhoneNumber()));
        //String[] details = extractFixedLineDetails(phoneNumberInfo.getPhoneNumber());
        String[] details = extractFixedLineDetails(phoneNumberInfo.getFormats().get(PhoneNumberFormat.NATIONAL.name()).toString());
        if (details != null && details.length > 0) {
            phoneNumberInfo.setOperator(details[2]);
            phoneNumberInfo.setStdCode(details[0]);
        }
        phoneNumberInfo.setType(PhoneNumberUtil.PhoneNumberType.FIXED_LINE.name());
        return phoneNumberInfo;
    }

    public static String extractMobileSeries(String number) {
        if (number != null && !number.trim().isEmpty()) {
            if (number.length() > 10) {
                return number.substring(number.length() - 10, number.length()).substring(0, 4);
            } else if (number.length() == 10) {
                return number.substring(0, 4);
            } else {
                return number.substring(0, 4);
            }
        } else {
            return "";
        }
    }

    public static String extractFixedLineOperator(String number) {
        String operator = null;
        Map<String, String> operators = new LinkedHashMap<String, String>();
        operators.put("2", "BSNL");
        operators.put("3", "RELIANCE");
        operators.put("4", "AIRTEL");
        operators.put("6", "TATAINDICOM");

        String numberLocation = "", stdCode = "";
        for (int i = 0; i <= 2; i++) {
            if (numberLocation == null || numberLocation.trim().isEmpty()) {
                stdCode = number.substring(1, 3 + i);
                numberLocation = StdConstants.getConstant(stdCode);
            } else {
                break;
            }
        }

        log.debug("numberLocation ::: " + numberLocation + " ::: stdCode ::: " + stdCode);
        if (numberLocation != null && !numberLocation.trim().isEmpty()) {
            operator = operators.get(number.substring(stdCode.length() + 1, stdCode.length() + 2));
        }
        return operator;
    }

    public static String[] extractFixedLineDetails(String number) {
        String[] details = new String[3];
        String numberLocation = "", stdCode = "";
        for (int i = 0; i <= 2; i++) {
            if (numberLocation == null || numberLocation.trim().isEmpty()) {
                stdCode = number.substring(1, 3 + i);
                numberLocation = StdConstants.getConstant(stdCode);
                details[0] = stdCode;
                details[1] = numberLocation;
            } else {
                break;
            }
        }

        log.debug("numberLocation ::: " + numberLocation + " ::: stdCode ::: " + stdCode);
        if (numberLocation != null && !numberLocation.trim().isEmpty()) {
            details[2] = FLOperatorConstants.getConstant(number.substring(stdCode.length() + 1, stdCode.length() + 2));
        }
        return details;
    }
}